#pragma ocne
#include <iostream>
#include <memory>
#include <functional>
#include <unordered_map>
#include "Comm.hpp"
#include "log.hpp"
#include "Epoll.hpp"
#include "Socket.hpp"

class Connection;
class TpcServer;
using func_t = std::function<void(std::weak_ptr<Connection>)>; // 用户缓冲区处理函数模板
using except_func_t = std::function<void(std::weak_ptr<Connection>)>;
static const uint16_t port = 8000;
static const int g_buff_size = 128;
// 设置et
uint32_t EVENT_IN = (EPOLLIN | EPOLLET);
uint32_t EVENt_OUT = (EPOLLOUT | EPOLLET);

class Connection
{
public:
    Connection(int sock)
    {
        _sock = sock;
    }

    ~Connection()
    {

    }

    void AppendInbuff(const std::string& message)
    {
        _inbuff += message;
    }

    void AppendOutbuff(const std::string& message)
    {
        _outbuff += message;
    }

    int Fd()
    {
        return _sock;
    }

    std::string& Inbuffer()  // for debug
    {
        return _inbuff;
    }

    std::string& Outbuffer()  // for debug
    {
        return _outbuff;
    }

    void SetHandler(func_t recv_cb, func_t send_cb, except_func_t except_cb)
    {
        _recv_cb = recv_cb;
        _send_cb = send_cb;
        _except_cb = except_cb;
    }

    void SetWeakPtr(std::weak_ptr<TpcServer> tcp_setver_ptr)
    {
        _tcp_server_ptr = tcp_setver_ptr;
    }

private:
    int _sock;
    std::string _inbuff;  // string不能二进制, 需要vector
    std::string _outbuff;
public:
    func_t _recv_cb;
    func_t _send_cb;
    except_func_t _except_cb;

    std::weak_ptr<TpcServer> _tcp_server_ptr;  // 回指向服务器
    std::string _ip;
    uint16_t _port;
};

class TpcServer :public std::enable_shared_from_this<TpcServer>, public nocopy
{
    static const int num = 64;

public:
    TpcServer(func_t OnMessage)
        : _listensocket_ptr(new Sock())
        , _epoll_ptr(new Epoll())
        , _OnMessage(OnMessage)
        , _quit(true)
    {
    }

    void AddConnection(int sock, uint32_t event, func_t recv_cb, func_t send_cb, \
                        except_func_t except_cb, const std::string& ip = "0.0.0.0", 
                        uint16_t port = 0)
    {
        // 1. 给sock创建connection对象, 将lstensock添加到connection中
        // 同时,listeinsock和connection放入_connections
        std::shared_ptr<Connection> new_con(new Connection(sock));
        new_con->SetWeakPtr(shared_from_this());  // 返回当前对象的shared_ptr
        new_con->SetHandler(recv_cb, send_cb, except_cb);
        new_con->_ip = ip;
        new_con->_port = port;
        // 2. 添加到map
        _connections.insert(std::make_pair(sock, new_con));
        // 3. 添加对应事件
        _epoll_ptr->EpollUpdate(EPOLL_CTL_ADD, sock, event);

    }

    void Init()
    {
        _listensocket_ptr->Socket();
        int opt = 1;
        setsockopt(_listensocket_ptr->Fd(), SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, 
                   &opt, sizeof(opt));
        // et模式需要非阻塞
        SetNonBlock(_listensocket_ptr->Fd());
        lg.logmessage(info, "listensock create success:%d", _listensocket_ptr->Fd());

        _listensocket_ptr->Bind(port);
        _listensocket_ptr->Listen();
        // 关联connection
        AddConnection(_listensocket_ptr->Fd(), EVENT_IN,
                      std::bind(&TpcServer::Accepter, this, std::placeholders::_1), 
                      nullptr, nullptr);
    }

    void Accepter(std::weak_ptr<Connection> con)
    {
        // 获取强引用对象, 检查是否销毁
        auto connection = con.lock();
        // 获取新链接
        while (true)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            // ::调用原生函数
            int sock = ::accept(connection->Fd(), (struct sockaddr *)&peer, &len);
            if (sock > 0)
            {
                char ipbuf[128];
                inet_ntop(AF_INET, &peer.sin_addr.s_addr, ipbuf, sizeof(ipbuf));
                uint16_t port = ntohs(peer.sin_port);
                lg.logmessage(info, "get a new clinet[%s:%d]:%d", ipbuf, port, sock);
                // 设置非阻塞
                SetNonBlock(sock);
                // 添加连接事件
                AddConnection(sock, EVENT_IN,\
                              std::bind(&TpcServer::Recver, this, std::placeholders::_1),\
                              std::bind(&TpcServer::Sender, this, std::placeholders::_1),\
                              std::bind(&TpcServer::Excepter, this, std::placeholders::_1),\
                              ipbuf, port);
            }
            else 
            {
                if (errno == EWOULDBLOCK)
                {
                    break;
                }
                else if (errno == EINTR)  // 信号中断
                {
                    continue;
                }
                else
                {
                    break;
                }
            }
        }
    }

    void Recver(std::weak_ptr<Connection> con)
    {
        if (con.expired())
            return;
        auto connec = con.lock();
        int sock = connec->Fd();

        while (true)
        {
            char buff[g_buff_size];
            memset(buff, 0, sizeof(buff));
            ssize_t n = recv(sock, buff, sizeof(buff) - 1, 0);  // 非阻塞读取
            if (n > 0)
            {
                connec->AppendInbuff(buff);
            }
            else if (n == 0)  // 错误处理
            {
                lg.logmessage(info, "sockfd:%d, client[%s:%d] quit", sock, connec->_ip.c_str(), connec->_port);
                connec->_except_cb(connec);
                return;
            }
            else
            {
                if (errno == EWOULDBLOCK)  // 读完
                {
                    break;
                }
                else if (errno == EINTR)
                {
                    continue;
                }
                else
                {
                    lg.logmessage(warning, "sockfd:%d, client[%s:%d] recv error", sock, connec->_ip.c_str(), connec->_port);
                    connec->_except_cb(connec);
                    return;
                }
            }
        }

        // 数据有了, 不一定安全 1.检测 2.如果有完整报文,处理
        _OnMessage(connec);
    }

    void Sender(std::weak_ptr<Connection> con)
    {
        if (con.expired())
            return;
        auto connection = con.lock();
        auto &outbuff = connection->Outbuffer();
        while (true)
        {
            ssize_t n = send(connection->Fd(), outbuff.c_str(), outbuff.size(), 0);
            if (n > 0)
            {
                outbuff.erase(0, n);
                if (outbuff.empty())
                {
                    break;
                }
            }
            else if (n == 0)
            {
                return;  // 没有发
            }
            else
            {
                if (errno == EWOULDBLOCK)
                {
                    break;
                }
                else if (errno == EINTR)
                {
                    continue;
                }
                else
                {
                    lg.logmessage(info, "sockfd:%d, client[%s:%d] recv error", connection->Fd(), connection->_ip.c_str(), connection->_port);
                    connection->_except_cb(connection);
                    return;
                }
            }
        }

        // 没发完, 开启对写事件关心
        if (!outbuff.empty())
        {
            EnableEvent(connection->Fd(), true, true);
        }
        else
        {
            EnableEvent(connection->Fd(), true, false);
        }
    }

    void EnableEvent(int sock, bool readable, bool writeable)
    {
        uint32_t evnet = 0;
        evnet |= ((readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET);
        _epoll_ptr->EpollUpdate(EPOLL_CTL_MOD, sock, evnet);
    }

    void Excepter(std::weak_ptr<Connection> con)
    {
        if (con.expired())
            return;
        auto connection = con.lock();
        int fd = connection->Fd();
        lg.logmessage(warning, "Excepter handler:%d, client[%s:%d] excepter error", connection->Fd(), connection->_ip.c_str(), connection->_port);

        // 1. 移除关心
        _epoll_ptr->EpollUpdate(EPOLL_CTL_DEL, fd, 0);
        // 2. 关闭文件
        lg.logmessage(debug, "close %d...", fd);
        close(fd);
        // 3. unordered_map中移除
        lg.logmessage(debug, "remove connection %d", fd);
        _connections.erase(fd);
    }

    bool IsSafeConnection(int sock)
    {
        auto it = _connections.find(sock);
        if (it == _connections.end())
        {
            return false;
        }
        else
        {
            return true;
        }  
    }

    void Dispatcher(int timeout)
    {
        int n = _epoll_ptr->EpollWait(_revs, num, timeout);
        for (int i = 0; i < n; i++)
        {
            int sock = _revs[i].data.fd;
            uint32_t event = _revs[i].events;

            // 统一将异常转换为读写问题
            if (event & EPOLLERR)
            {
                event |= (EPOLLIN | EPOLLOUT);
            }

            if (event & EPOLLHUP)
            {
                event |= (EPOLLIN | EPOLLOUT);
            }

            if ((event & EPOLLIN) && IsSafeConnection(sock))
            {
                if (_connections[sock]->_recv_cb)
                {
                    _connections[sock]->_recv_cb(_connections[sock]);
                }
            }

            if ((event & EPOLLOUT) && IsSafeConnection(sock))
            {
                if (_connections[sock]->_send_cb)
                {
                    _connections[sock]->_send_cb(_connections[sock]);
                }
            }
        }
    }

    void Loop()
    {
        _quit = false;

        while (!_quit)
        {
            Dispatcher(3000);
            PrintConnection();
        }

        _quit = true;
    }

    void PrintConnection()
    {
        std::cout << "_connection list: ";
        for (auto &con: _connections)
        {
            std::cout << con.second->Fd() << ",";
        }

        std::cout << std::endl;
    }

private:
    std::shared_ptr<Sock> _listensocket_ptr;  // 监听socket, 可以移到外部
    std::shared_ptr<Epoll> _epoll_ptr;  // 内核
    std::unordered_map<int, std::shared_ptr<Connection>> _connections;
    struct epoll_event _revs[num];
    func_t _OnMessage;
    bool _quit;
};